﻿using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Markup;
using System.Windows.Navigation;
using FanaticRock.Theme.Interfaces;
using FanaticRock.Theme.Utils;

namespace FanaticRock.Theme.Helpers
{
    ///<summary>用于支持本项目所有的导航相关的服务。</summary>
    public class NavigationHelper
    {
        #region extra data

        public static object GetExtraData(DependencyObject obj)
        {
            return (object)obj.GetValue(ExtraDataProperty);
        }

        public static void SetExtraData(DependencyObject obj, object value)
        {
            obj.SetValue(ExtraDataProperty, value);
        }

        // Using a DependencyProperty as the backing store for ExtraData.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ExtraDataProperty =
            DependencyProperty.RegisterAttached("ExtraData", typeof(object), typeof(NavigationHelper), new PropertyMetadata(null));



        #endregion

        #region navigated

        public static Uri GetHyperlinkUri(DependencyObject obj)
        {
            return (Uri)obj.GetValue(HyperlinkUriProperty);
        }

        public static void SetHyperlinkUri(DependencyObject obj, Uri value)
        {
            obj.SetValue(HyperlinkUriProperty, value);
        }

        // Using a DependencyProperty as the backing store for HyperlinkUri.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty HyperlinkUriProperty =
            DependencyProperty.RegisterAttached("HyperlinkUri", typeof(Uri), typeof(NavigationHelper), new PropertyMetadata(null));


        #endregion

        #region cached

        public static bool GetIsNavigateCached(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsNavigateCachedProperty);
        }

        public static void SetIsNavigateCached(DependencyObject obj, bool value)
        {
            obj.SetValue(IsNavigateCachedProperty, value);
        }

        // Using a DependencyProperty as the backing store for IsNavigateCached.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsNavigateCachedProperty =
            DependencyProperty.RegisterAttached("IsNavigateCached", typeof(bool), typeof(NavigationHelper), new PropertyMetadata(false));


        #endregion

        #region is navigated

        public static bool GetIsNavigateToHere(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsNavigateToHereProperty);
        }

        public static void SetIsNavigateToHere(DependencyObject obj, bool value)
        {
            obj.SetValue(IsNavigateToHereProperty, value);
        }

        // Using a DependencyProperty as the backing store for IsNavigateToHere.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsNavigateToHereProperty =
            DependencyProperty.RegisterAttached("IsNavigateToHere", typeof(bool), typeof(NavigationHelper), new PropertyMetadata(false));

        #endregion

        #region navigatedfrom

        public static Frame GetNavigatedFrom(DependencyObject obj)
        {
            return (Frame)obj.GetValue(NavigatedFromProperty);
        }

        public static void SetNavigatedFrom(DependencyObject obj, Frame value)
        {
            obj.SetValue(NavigatedFromProperty, value);
        }

        // Using a DependencyProperty as the backing store for NavigatedFrom.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty NavigatedFromProperty =
            DependencyProperty.RegisterAttached("NavigatedFrom", typeof(Frame), typeof(NavigationHelper), new PropertyMetadata(null)
            {
                PropertyChangedCallback = OnNavigatedFromChanged
            });

        private static void OnNavigatedFromChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var oldFrame = e.OldValue as Frame;
            var newFrame = e.NewValue as Frame;

            if (oldFrame != null)
            {
                oldFrame.Navigated -= FrameNavigated;
                _frameNavigateToList.Remove(oldFrame);
            }
            if (newFrame != null)
            {
                newFrame.Navigated += FrameNavigated;
                UpdateContainerNavigationState(newFrame, newFrame.Source);

                _frameNavigateToList.TryAdd(newFrame, d);
            }

            if (d is UIElement uiElement)
            {
                uiElement.RemoveHandler(ButtonBase.ClickEvent, _onRoutedEventHandler);
                uiElement.AddHandler(ButtonBase.ClickEvent, _onRoutedEventHandler);
            }
        }

        private static readonly RoutedEventHandler _onRoutedEventHandler = new RoutedEventHandler(OnButtonClick);

        ///<summary>在容器中按下带有导航相关信息的按钮时可触发此事件。</summary>
        private static void OnButtonClick(object sender, RoutedEventArgs args)
        {
            if (sender is UIElement uiElement)
            {
                var button = args.OriginalSource as Button;
                if (button == null) return;

                var hyperlink = GetHyperlinkUri(button);
                if (hyperlink == null) return;

                var frame = GetNavigatedFrom(uiElement);
                var navigationService = default(NavigationService);
                if (frame == null)
                    navigationService = NavigationService.GetNavigationService(button);
                else
                    navigationService = frame.NavigationService;
                if (navigationService == null) return;

                var extraData = GetExtraData(button);

                var extendNavigationService =
                    ExtendNavigationServices.GetExtendNavigationService(navigationService);

                extendNavigationService.Navigate(hyperlink, extraData);


                //frame.Navigate();
            }
        }

        private static void FrameNavigated(object sender, NavigationEventArgs e)
        {
            var frame = sender as Frame;
            if (frame == null) return;
            var extendNavigationService =
              ExtendNavigationServices.GetExtendNavigationService(frame.NavigationService);

            var uiElement = e.Content as FrameworkElement;
            if (frame.CanGoBack)
                frame.RemoveBackEntry();
            ClearStack(frame);

            if (uiElement != null)
            {
                uiElement.DataContext = frame.DataContext;
                if (e.ExtraData != null)
                {
                    if (uiElement is IWithParameter withParameter)
                    {
                        withParameter.Receive(e.ExtraData);
                    }
                }
                else
                {
                    extendNavigationService.CacheIt(e.Uri, uiElement);
                }

            }

            if (_frameNavigateToList.TryGetValue(frame, out var dpobject))
            {
                var uri = e.Uri;

                if (uri == null)
                {
                    uri = extendNavigationService.GetUriByContent(e.Content as FrameworkElement);
                }

                if (uri != null)
                    UpdateContainerNavigationState(dpobject, uri);
            }
        }

        private static void ClearStack(Frame? frame)
        {
            var backStack = frame.BackStack;

            var backCount = 0;

            //var forwardStack = frame.ForwardStack;
            //var forwardCount = 0;   

            if (backStack != null)
            {
                foreach (var item in backStack)
                {
                    backCount++;
                }
            }

            if (backCount > 10)
            {

                foreach (var item in backStack)
                {
                    backCount++;
                }
            }
        }

        private static void UpdateContainerNavigationState(object sender, Uri uri)
        {
            if (sender is IAddChild childContainer)
            {
                var dpobjects = GetChildItems(childContainer);
                UpdateContainerNavigationStateCore(dpobjects, uri);
            }
        }

        private static IEnumerable<DependencyObject> GetChildItems(IAddChild childContainer)
        {
            if (childContainer is Panel panel)
            {
                var dpobjects = panel.Children.OfType<DependencyObject>();

                return dpobjects;
            }
            else if (childContainer is ItemsControl itemsControl)
            {
                var gen = itemsControl.ItemContainerGenerator;
                var items = gen.Items;
                var dpobjects = new List<DependencyObject>();
                foreach (var item in items)
                {
                    var container = gen.ContainerFromItem(item);
                    if (container == null) continue;
                    dpobjects.Add(container);
                }

                return dpobjects;
            }

            return new DependencyObject[0];
        }

        private static void UpdateContainerNavigationStateCore(IEnumerable<DependencyObject> dpobjects, Uri currentUri)
        {
            var originString = currentUri.OriginalString;
            foreach (var dpobject in dpobjects)
            {
                var hyperlinkUri = GetHyperlinkUri(dpobject);
                if (hyperlinkUri == null) continue;
                var orignHyperlinkUri = hyperlinkUri.OriginalString;

                var isMatched = IsOriginStringMatchedHyperlinkString(originString, orignHyperlinkUri);

                SetIsNavigateToHere(dpobject, isMatched);
            }
        }
        private static bool IsOriginStringMatchedHyperlinkString(string uriString, string hyperlinkString)
        {
            if (string.IsNullOrWhiteSpace(uriString)) return false;
            if (string.IsNullOrWhiteSpace(hyperlinkString)) return false;
            if (string.Equals(uriString, hyperlinkString, StringComparison.OrdinalIgnoreCase)) return true;
            if (uriString.EndsWith(hyperlinkString, StringComparison.OrdinalIgnoreCase)) return true;
            return false;
        }

        #endregion

        #region navigation map

        private static Dictionary<Frame, DependencyObject> _frameNavigateToList = new Dictionary<Frame, DependencyObject>();

        #endregion

        #region navigatedhost
        // 判定是否是导航容器。
        public static bool GetIsNavigationHost(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsNavigationHostProperty);
        }

        public static void SetIsNavigationHost(DependencyObject obj, bool value)
        {
            obj.SetValue(IsNavigationHostProperty, value);
        }

        // Using a DependencyProperty as the backing store for IsNavigationHost.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsNavigationHostProperty =
            DependencyProperty.RegisterAttached("IsNavigationHost", typeof(bool), typeof(NavigationHelper), new PropertyMetadata(false)
            {
                PropertyChangedCallback = OnIsNavigationHostChanged
            });

        private static void OnIsNavigationHostChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is Frame frame)
            {
                if (e.NewValue is bool bvalue)
                {
                    if (bvalue)
                    {
                        frame.AddHandler(ButtonBase.ClickEvent, _onRoutedEventHandler);
                    }
                    else
                    {
                        frame.RemoveHandler(ButtonBase.ClickEvent, _onRoutedEventHandler);
                    }
                }
            }
        }

        #endregion
    }
}
